`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/03/18 12:53:12
// Design Name:
// Module Name: tx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////


module uart_tx(
     input clk,
     input rst,
     input [7:0] tx_data,
     input tx_data_vld,
     output ready,
     output reg tx
   );
  localparam SYS_CLK_FREQ = 'd50_000_000;
  localparam BAUD_RATE = 'd9600;
  localparam MAX_1bit = SYS_CLK_FREQ / BAUD_RATE;
  localparam IDLE = 4'b0001,
             START =4'b0010,
             DATA = 4'b0100,
             STOP = 4'b1000;

  reg [4:0] cs;
  reg [4:0] ns;

  reg [19:0] cnt_baud;
  wire add_cnt_baud;
  wire end_cnt_baud;

  reg [2:0] cnt_bit;
  wire add_cnt_bit;
  wire end_cnt_bit;

  reg [3:0] bit_max;
  reg [7:0] tx_data_r;

  always @(posedge clk or posedge rst)
  begin
    if (rst)
    begin
      cnt_baud <= 20'd0;
    end
    else
    begin
      if(add_cnt_baud)
      begin
        if(end_cnt_baud)
          cnt_baud <= 20'd0;
        else
          cnt_baud <= cnt_baud + 1;
      end
    end
  end
  assign add_cnt_baud = cs != IDLE;
  assign end_cnt_baud = add_cnt_baud && (cnt_baud == MAX_1bit - 1);

  always @(posedge clk or posedge rst)
  begin
    if(rst)
      cnt_bit <= 3'd0;
    else
    begin
      if(add_cnt_bit)
      begin
        if(end_cnt_bit)
          cnt_bit <= 3'd0;
        else
          cnt_bit <= cnt_bit + 1;
      end
    end
  end
  assign add_cnt_bit = end_cnt_baud;
  assign end_cnt_bit = add_cnt_bit && (cnt_bit == bit_max - 1);

  always @(*)
  begin
    case(cs)
      IDLE:   bit_max = 'd0;
      START:  bit_max = 'd1;
      DATA:   bit_max = 'd8;
      STOP:   bit_max = 'd1;
      default:bit_max = 'd0;
    endcase
  end
  always @(posedge clk or posedge rst) begin
    if(rst)
        cs <= IDLE;
    else
        cs <= ns; 
  end
  
  always @(*)
  case (cs)
    IDLE :    begin
      if((cs == IDLE) && tx_data_vld)
        ns = START;
      else
        ns = cs;
    end    START:    begin
      if((cs == START) && end_cnt_bit)
        ns = DATA;
      else
        ns = cs;
    end    DATA:    begin
      if((cs == DATA) && end_cnt_bit)
        ns = STOP;
      else
        ns = cs;
    end    STOP:    begin
        if((cs == STOP) && end_cnt_bit)
            ns = IDLE;
        else
            ns = cs;
    end    default:        ns = cs;
    endcase

    always @(posedge clk or posedge rst) begin
        if(rst)
            tx_data_r <= 0;
        else begin
            if(tx_data_vld)
                tx_data_r <= tx_data;
            else
                tx_data_r <= tx_data_r;
        end
    end

    always @(*) begin
        case (cs)
            IDLE : tx = 1'b1;
            START : tx = 1'b0;
            DATA : tx =  tx_data_r[cnt_bit];
            STOP : tx = 1'b1;
            default: tx = 1'b1;
        endcase
    end

    assign ready = cs == IDLE;
endmodule
